﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate;
using Thaire.Investment.BL.DTO;
using Thaire.Investment.BL.Filter;
using Thaire.Investment.BL.ScreenData;
using Thaire.Investment.Domains.Master;
using Thaire.Investment.Domains.Transaction;
using Thaire.Investment.Repository;

namespace Thaire.Investment.BL
{
    public class DepositBL
    {
        ISession _session;

        public DepositBL(ISession session)
        {
            _session = session;
        }

        public List<DepositHeaderDTO> SearchDeposit(SearchDepositFilter searchData)
        {
            IQueryOver<DepositHeader, DepositHeader> query = _session.QueryOver<DepositHeader>()
               .Where(x => x.DepositHeaderID == x.DepositHeaderID);

            if (searchData.Company.HasValue)
                query.And(x => x.DepositCompanyID == searchData.Company.Value);
            if (searchData.DepositType.HasValue)
                query.And(x => x.AssetClassID == searchData.DepositType.Value);
            if (searchData.FinanceType.HasValue)
                query.And(x => x.FinancialInstrumentTypeID == searchData.FinanceType.Value);
            if (!String.IsNullOrEmpty(searchData.BankName))
                query.And(x => x.BankName == searchData.BankName);
            if (!String.IsNullOrEmpty(searchData.Number))
                query.And(x => x.AccountVoucherNO == searchData.Number);
            query.OrderBy(x => x.CreateDate);

            var headers = new List<DepositHeader>(query.List());

            var items = new List<DepositHeaderDTO>();
            foreach (var header in headers)
            {
                var item = new DepositHeaderDTO();
                var refRepo = new ReferenceCodeRepo(_session);
                item.Header = header;
                if (header.AssetClassID.HasValue)
                    item.DepositType = new AssetClassRepo(_session).GetByID(header.AssetClassID.Value).AssetClassEN;
                if (header.FinancialInstrumentTypeID.HasValue)
                    item.FinanceInstrument = refRepo.GetByID(header.FinancialInstrumentTypeID.Value).ValueTH;
                items.Add(item);
            }

            return items;
        }

        public DepositHeaderScreenData GetDepositHeaderScreenData(int? headerId)
        {
            var data = new DepositHeaderScreenData();
            var invRepo = new InvolvedPartyTypeRepo(_session);
            var refRepo = new ReferenceCodeRepo(_session);

            if (headerId != null)
            {
                data.HeaderDTO = new DepositHeaderDTO();
                var header = new DepositHeaderRepo(SessionManager.CurrentSession).GetByID(headerId.Value);
                data.HeaderDTO.Header = header;
                data.HeaderDTO.Transactions = new DepositTransactionRepo(SessionManager.CurrentSession).GetListByHeaderID(headerId.Value);
                data.HeaderDTO.InvolvedPartyType = invRepo.GetByName(header.InvolvedPartyTypeName);
                if (header.AssetClassID.HasValue)
                    data.HeaderDTO.DepositType = refRepo.GetByID(header.AssetClassID.Value).ValueTH;
                if (header.FinancialInstrumentTypeID.HasValue)
                    data.HeaderDTO.FinanceInstrument = refRepo.GetByID(header.FinancialInstrumentTypeID.Value).ValueTH;
                var listRatings = new RatingHistoryRepo(_session).GetByDepositHeaderID(header.DepositHeaderID);
                if (listRatings != null && listRatings.Any())
                    data.HeaderDTO.Rating = listRatings[listRatings.Count - 1];
                if (!string.IsNullOrEmpty(header.BranchCode))
                    data.Branchs = new BankRepo(_session).GetBranch(header.BankCode);
            }
            data.Companies = new CompanyRepo(_session).GetAll().ToList();
            data.InvolvedPartyTypes = invRepo.GetAll().ToList();

            var codes = refRepo.GetByDomain(ReferenceCodeRepo.DOMAIN_FINANCIAL_INSTRUMENTS, true);

            //find asset class
            if (codes != null)
            {
                foreach (var code in codes)
                {
                    var type = Thaire.Investment.Common.CommonUtil.GetInstrumentType(code.Key);
                    if (type == Common.InstrumentType.DepositAtFinancial)
                    {
                        data.DepositTypes = new AssetClassRepo(_session).GetListByFinancialInstrumentsReferenceCodeId(code.ID).ToList();
                    }
                    break;
                }
            }

            data.FinanceTypes = refRepo.GetByDomain(ReferenceCodeRepo.DEPOSIT_TYPE, true);
            data.Banks = new BankRepo(_session).GetAllBank();
            data.IssueRatings = new CreditAgencyRepo(_session).GetAll().ToList();
            return data;
        }

        public DepositHeaderDTO InsertDepositHeader(DepositHeaderDTO headerDTO)
        {
            var data = headerDTO.Header;
            var transactionDT = SessionManager.SystemDate;
            var comRepo = new CompanyRepo(_session);
            var eqRepo = new DepositHeaderRepo(_session);

            if (data.DepositCompanyID.HasValue)
            {
                var com = comRepo.GetByID(data.DepositCompanyID.Value);
                if (com != null)
                    data.DepositCompanyName = com.CompanyNameTh;
            }

            FillBankData(data);

            data.CreateDate = transactionDT;
            eqRepo.Save(data);
            _session.Flush();

            headerDTO.Header = data;

            InsertRatingHistory(headerDTO, transactionDT);
            InsertDepositInterPaymentDate(headerDTO.Header, transactionDT);

            var result = new DepositHeaderDTO
            {
                Header = data,
                Transactions = new DepositTransactionRepo(SessionManager.CurrentSession).GetListByHeaderID(data.DepositHeaderID)
            };
            var listRatings = new RatingHistoryRepo(_session).GetByDepositHeaderID(data.DepositHeaderID);
            if (listRatings != null && listRatings.Any())
                result.Rating = listRatings[listRatings.Count - 1];

            return result;
        }

        private void InsertDepositInterPaymentDate(DepositHeader depositHeader, DateTime transactionDT)
        {
            var paymentDate = depositHeader.InterestPaymentDate;
            var interestRepo = new DepositInterestRepo(_session);
            if (paymentDate.HasValue)
            {
                List<DepositInterest> items = interestRepo.GetByDepositHeaderID(depositHeader.DepositHeaderID);
                if (items != null && items.Any())
                {
                    DepositInterest latestItem = items[items.Count - 1];

                    if (latestItem != null)
                    {
                        interestRepo.Evict(latestItem);
                        if (paymentDate != latestItem.InterestPaymentDate)
                        {
                            var interest = new DepositInterest();
                            interest.DepositHeaderID = depositHeader.DepositHeaderID;
                            interest.InterestPaymentDate = paymentDate;
                            interestRepo.Save(interest);
                        }
                    }
                    else
                    {
                        var interest = new DepositInterest();
                        interest.DepositHeaderID = depositHeader.DepositHeaderID;
                        interest.InterestPaymentDate = paymentDate;
                        interestRepo.Save(interest);
                    }
                }
            }
        }

        private void InsertRatingHistory(DepositHeaderDTO dto, DateTime transactionDT)
        {
            RatingHistory rate = dto.Rating;
            var ratingHistoryRepo = new RatingHistoryRepo(_session);
            if (rate != null)
            {
                RatingHistory oldRatingHis = ratingHistoryRepo.GetByID(rate.RatingHistoryId);
                if (oldRatingHis != null)
                {
                    ratingHistoryRepo.Evict(oldRatingHis);
                    if (dto.Rating.DataKey() != oldRatingHis.DataKey())
                    {
                        ratingHistoryRepo.Save(rate);
                    }
                }
                else
                {
                    rate.FinancialInstrumentId = dto.Header.DepositHeaderID;
                    rate.FinancialInstrumentType = "DEPOSIT";
                    rate.CreateTs = transactionDT;
                    ratingHistoryRepo.Save(rate);
                }
                //_session.Flush();
            }
        }

        public DepositInterest InsertDepositInterest(DepositInterest data)
        {
            var repo = new DepositInterestRepo(_session);
            repo.Save(data);
            //_session.Flush();

            return data;
        }

        public void DeleteDepositInterest(int interestID)
        {
            var repo = new DepositInterestRepo(_session);
            repo.DeleteByID(interestID);
        }

        public DepositHeaderDTO UpdateDepositHeader(DepositHeaderDTO headerDTO)
        {
            var data = headerDTO.Header;
            var transactionDT = SessionManager.SystemDate;
            var depositRepo = new DepositHeaderRepo(_session);
            var tempHeader = depositRepo.GetByID(data.DepositHeaderID);

            if (tempHeader == null)
                throw new Exception("Deposit header not found : " + data.DepositHeaderID);
            _session.Evict(tempHeader);

            if (data.DepositCompanyID.HasValue)
            {
                var comRepo = new CompanyRepo(_session);
                var com = comRepo.GetByID(data.DepositCompanyID.Value);
                if (com != null)
                    data.DepositCompanyName = com.CompanyNameTh;
            }

            FillBankData(data);

            data.ModifyDate = transactionDT;
            depositRepo.Update(data);
            _session.Flush();


            InsertRatingHistory(headerDTO, transactionDT);
            InsertDepositInterPaymentDate(headerDTO.Header, transactionDT);

            var result = new DepositHeaderDTO
            {
                Header = data,
                Transactions = new DepositTransactionRepo(SessionManager.CurrentSession).GetListByHeaderID(data.DepositHeaderID)
            };
            var listRatings = new RatingHistoryRepo(_session).GetByDepositHeaderID(data.DepositHeaderID);
            if (listRatings != null && listRatings.Any())
                result.Rating = listRatings[listRatings.Count - 1];

            return result;
        }

        private void FillBankData(DepositHeader data)
        {
            if (!string.IsNullOrEmpty(data.BankCode) && !string.IsNullOrEmpty(data.BranchCode))
            {
                Bank bank = null;
                var bRepo = new BankRepo(_session);

                var banks = bRepo.GetAllBank();
                if (banks != null)
                {
                    bank = banks.Where(x => x.BankCode == data.BankCode && x.BankBranchCode == "0000").SingleOrDefault();
                    if (bank != null)
                    {
                        data.BankName = bank.BankNameTh;
                    }
                }

                var branchs = bRepo.GetBranch(data.BankCode);
                if (branchs != null)
                {
                    bank = branchs.Where(x => x.BankBranchCode == data.BranchCode).SingleOrDefault();
                    if (bank != null)
                    {
                        data.BranchName = bank.BankNameTh;
                    }
                }
            }
        }

        public DepositTransaction InsertDepositTransaction(DepositTransaction data)
        {
            data.CreateDate = SessionManager.SystemDate;
            new DepositTransactionRepo(_session).Save(data);
            //_session.Flush();
            return data;
        }

        public DepositTransaction UpdateDepositTransaction(DepositTransaction data)
        {
            var repo = new DepositTransactionRepo(_session);
            var temp = repo.GetByID(data.DepositTransactionID);
            if (temp == null)
                throw new Exception(string.Format("Deposit Transaction ID : {0} not found.", data.DepositTransactionID));
            ////concurrency control
            //if (temp.ModifyDate  != data.ModifyDate)
            //    throw new Exception(string.Format("Update Timestamp failed. (Concurrency control Error {0} : {1})", temp.ModifyDate, data.ModifyDate));
            _session.Evict(temp);
            data.ModifyDate = SessionManager.SystemDate;
            repo.Update(data);
            //_session.Flush();
            return data;
        }

        public DepositTransactionScreenData GetDepositTransactionScreenData(int? headerId, int? transactionId, string transactionType)
        {
            var data = new DepositTransactionScreenData();
            if (transactionId.HasValue)
            {
                data.Transaction = new DepositTransactionRepo(SessionManager.CurrentSession).GetByID(transactionId.Value);
                if (headerId.HasValue && headerId.Value != data.Transaction.DepositHeaderID)
                    throw new Exception(string.Format("Deposit {0} is not equals the parameter value : {1}", data.Transaction.DepositTransactionID, headerId.Value));
            }
            if (headerId.HasValue)
                data.Header = new DepositHeaderRepo(SessionManager.CurrentSession).GetByID(headerId.Value);
            else
            {
                if (transactionId.HasValue && data.Transaction != null)
                {
                    data.Header = new DepositHeaderRepo(SessionManager.CurrentSession).GetByID(data.Transaction.DepositHeaderID.Value);
                }
            }

            data.InvolvedType = new InvolvedPartyTypeRepo(_session).GetByName(data.Header.InvolvedPartyTypeName);

            if (transactionType == "Guarantee")
            {
                data.Obligations = new ObligationRepo(_session).GetAll().ToList();
                data.ObligationNOs = new ObligationNORepo(_session).GetAll().ToList();
            }
            else
            {
                data.PaymentTypes = new PaymentTypeRepo(_session).GetAll().ToList();
            }

            return data;
        }

        public DepositInterestScreenData GetDepositInterestScreenData(int headerId)
        {
            var data = new DepositInterestScreenData();
            data.Header = new DepositHeaderRepo(_session).GetByID(headerId);
            data.ListInterest = new DepositInterestRepo(_session).GetByDepositHeaderID(headerId);
            return data;
        }
    }
}
